home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / New System Software Extensions / QuickDraw™ GX v1.0ß2 / Interfaces & Libraries / graphics libraries / selection library.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-29  |  35.9 KB  |  1,074 lines  |  [TEXT/MPS ]

  1. /* selection library.c -- SelectionHandle manipulation routines.
  2.     
  3. CHANGE LOG
  4.  
  5. Date      Person      Action
  6. ----      ------      ------
  7.  
  8. 911031    DGO       • Created module.
  9. 920130    DGO       • Canonicalized order for range selections.
  10.  
  11. */
  12.  
  13. /* Copyright ©1991 Apple Computer, Inc.  All rights reserved. */
  14.  
  15. #include <Types.h>
  16. #include <Memory.h>
  17.   #include <OSUtils.h>
  18.  
  19. #ifndef selectionLibraryIncludes
  20. #include "selection library.h"
  21. #endif
  22.  
  23. #ifndef layoutTypesIncludes
  24. #include "layout types.h"
  25. #endif
  26.  
  27. #ifndef layoutRoutinesIncludes
  28. #include "layout routines.h"
  29. #endif
  30.  
  31. #ifndef graphicsRoutinesIncludes
  32. #include "graphics routines.h"
  33. #endif
  34.  
  35. #define siSize sizeof(Selection)
  36. #define sioSize (siSize + sizeof(SelectionOffsetRange))
  37.  
  38. #define MyMin(a,b) (((a) < (b)) ? (a) : (b))
  39. #define MyMax(a,b) (((a) > (b)) ? (a) : (b))
  40.  
  41. typedef enum {
  42.   unionAll,
  43.   unionOne,
  44.   firstIsLow,
  45.   secondIsLow
  46.   } UnionOneState;
  47.  
  48. /* ---------------------------------------------------------------------------------- */
  49.  
  50. /* NewEmptySelection creates a Selection of type emptySelection. */
  51.  
  52. SelectionHandle NewEmptySelection()
  53.   {
  54.   Handle        h;
  55.   SelectionPtr  sip;
  56.   if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  57.   sip = (SelectionPtr) *h;
  58.   sip->type = emptySelection;
  59.   return ((SelectionHandle) h);
  60.   } /* NewEmptySelection */
  61.  
  62. /* ---------------------------------------------------------------------------------- */
  63.  
  64. /* NewCaretSelection creates a Selection of type simpleCaret and fills it with the
  65.     specified offset and leadingEdge information. */
  66.  
  67. SelectionHandle NewCaretSelection(SelectionOffset offset, long leadingEdge)
  68.   {
  69.   Handle        h;
  70.   SelectionPtr  sip;
  71.   if ((h = NewHandle(siSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  72.   sip = (SelectionPtr) *h;
  73.   sip->type = simpleCaret;
  74.   sip->data.caret.offset = offset;
  75.   sip->data.caret.leadingEdge = (short) leadingEdge;
  76.   return ((SelectionHandle) h);
  77.   } /* NewCaretSelection */
  78.  
  79. /* ---------------------------------------------------------------------------------- */
  80.  
  81. /* NewRangeSelection creates a Selection of type simpleRange and sets it to a single
  82.     range, using the specified data. */
  83.  
  84. SelectionHandle NewRangeSelection(SelectionOffsetRange *range)
  85.   {
  86.   Handle                h;
  87.   SelectionOffsetRange  sortedRange;
  88.   SelectionPtr          sip;
  89.   if (range == nil) {GXPostGraphicsError(parameter_is_nil); return nil;}
  90.   if (range->minOffset == range->maxOffset)
  91.     return NewCaretSelection(range->minOffset, false);
  92.   else if (range->minOffset > range->maxOffset)
  93.     {
  94.     sortedRange.minOffset = range->maxOffset;
  95.     sortedRange.maxOffset = range->minOffset;
  96.     range = &sortedRange;
  97.     }
  98.   if ((h = NewHandle(sioSize)) == nil) {GXPostGraphicsError(out_of_memory); return nil;}
  99.   sip = (SelectionPtr) *h;
  100.   sip->type = simpleRange;
  101.   sip->data.range.rangeCount = 1;
  102.   sip->data.range.ranges[0] = *range;
  103.   return ((SelectionHandle) h);
  104.   } /* NewRangeSelection */
  105.  
  106. /* ---------------------------------------------------------------------------------- */
  107.  
  108. /* NewFullSelection creates a Selection of type simpleRange and sets it to a single
  109.     range, with both ends set to the selectionExtremeEdge value. */
  110.  
  111. SelectionHandle NewFullSelection()
  112.   {
  113.   SelectionOffsetRange  myRange;
  114.   myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  115.   return NewRangeSelection(&myRange);
  116.   } /* NewFullSelection */
  117.  
  118. /* ---------------------------------------------------------------------------------- */
  119.  
  120. /* NewStartSelection creates a Selection of type simpleRange and sets it to a single
  121.     range, with the start set to selectionExtremeEdge and the end set to the specified
  122.     offset. */
  123.  
  124. SelectionHandle NewStartSelection(SelectionOffset toOffset)
  125.   {
  126.   SelectionOffsetRange  myRange;
  127.   myRange.minOffset = selectionExtremeEdge;
  128.   myRange.maxOffset = toOffset;
  129.   return NewRangeSelection(&myRange);
  130.   } /* NewStartSelection */
  131.  
  132. /* ---------------------------------------------------------------------------------- */
  133.  
  134. /* NewEndSelection creates a Selection of type simpleRange and sets it to a single
  135.     range, with the start set to the specified offset and the end set to the
  136.     selectionExtremeEdge value. */
  137.  
  138. SelectionHandle NewEndSelection(SelectionOffset fromOffset)
  139.   {
  140.   SelectionOffsetRange  myRange;
  141.   myRange.minOffset = fromOffset;
  142.   myRange.maxOffset = selectionExtremeEdge;
  143.   return NewRangeSelection(&myRange);
  144.   } /* NewEndSelection */
  145.  
  146. /* ---------------------------------------------------------------------------------- */
  147.  
  148. /* SetEmptySelection takes an existing Selection and removes its contents, turning it
  149.     into a Selection of type emptySelection. */
  150.  
  151. void SetEmptySelection(SelectionHandle selection)
  152.   {
  153.   SelectionPtr  sip;
  154.   if (selection == nil || (sip = *selection) == nil)
  155.     {GXPostGraphicsError(parameter_is_nil); return;}
  156.   sip->type = emptySelection;
  157.   } /* SetEmptySelection */
  158.  
  159. /* ---------------------------------------------------------------------------------- */
  160.  
  161. /* SetCaretSelection takes an existing Selection and removes its contents, turning it
  162.     into a Selection of type simpleCaret and copying the specified data into it. */
  163.  
  164. void SetCaretSelection(SelectionHandle selection, SelectionOffset offset, long leadingEdge)
  165.   {
  166.   SelectionPtr  sip;
  167.   if (selection == nil || (sip = *selection) == nil)
  168.     {GXPostGraphicsError(parameter_is_nil); return;}
  169.   sip->type = simpleCaret;
  170.   sip->data.caret.offset = offset;
  171.   sip->data.caret.leadingEdge = (short) leadingEdge;
  172.   } /* SetCaretSelection */
  173.  
  174. /* ---------------------------------------------------------------------------------- */
  175.  
  176. /* SetRangeSelection takes an existing Selection and removes its contents, turning it
  177.     into a Selection of type simpleRange containing a single range with the specified
  178.     offsets. */
  179.  
  180. void SetRangeSelection(SelectionHandle selection, SelectionOffsetRange *range)
  181.   {
  182.   SelectionOffsetRange  sortedRange;
  183.   SelectionPtr          sip;
  184.   if (selection == nil || (sip = *selection) == nil)
  185.     {GXPostGraphicsError(parameter_is_nil); return;}
  186.   if (range->minOffset == range->maxOffset)
  187.     {
  188.     SetCaretSelection(selection, range->minOffset, false);
  189.     return;
  190.     }
  191.   if (GetHandleSize((Handle) selection) < sioSize)
  192.     { /* need to grow handle */
  193.     SetHandleSize((Handle) selection, sioSize);
  194.     if (MemError())
  195.       {GXPostGraphicsError(out_of_memory); return;}
  196.     sip = *selection; /* might have moved... */
  197.     } /* end need to grow handle */
  198.   if (range->minOffset > range->maxOffset)
  199.     {
  200.     sortedRange.minOffset = range->maxOffset;
  201.     sortedRange.maxOffset = range->minOffset;
  202.     range = &sortedRange;
  203.     }
  204.   sip->type = simpleRange;
  205.   sip->data.range.rangeCount = 1;
  206.   sip->data.range.ranges[0] = *range;
  207.   } /* SetRangeSelection */
  208.  
  209. /* ---------------------------------------------------------------------------------- */
  210.  
  211. /* SetFullSelection takes an existing Selection and removes its contents, turning it
  212.     into a Selection of type simpleRange containing a single range with both offsets
  213.     set to the selectionExtremeEdge value. */
  214.  
  215. void SetFullSelection(SelectionHandle selection)
  216.   {
  217.   SelectionOffsetRange  myRange;
  218.   myRange.minOffset = myRange.maxOffset = selectionExtremeEdge;
  219.   SetRangeSelection(selection, &myRange);
  220.   } /* SetFullSelection */
  221.  
  222. /* ---------------------------------------------------------------------------------- */
  223.  
  224. /* SetStartSelection takes an existing Selection and removes its contents, turning it into
  225.     a Selection of type simpleRange containing a single range with the start offset set
  226.     to the selectionExtremeEdge value and the end offset set to the specified value. */
  227.  
  228. void SetStartSelection(SelectionHandle selection, SelectionOffset toOffset)
  229.   {
  230.   SelectionOffsetRange  myRange;
  231.   myRange.minOffset = selectionExtremeEdge;
  232.   myRange.maxOffset = toOffset;
  233.   SetRangeSelection(selection, &myRange);
  234.   } /* SetStartSelection */
  235.  
  236. /* ---------------------------------------------------------------------------------- */
  237.  
  238. /* SetEndSelection takes an existing Selection and removes its contents, turning it into
  239.     a Selection of type simpleRange containing a single range with the start offset set
  240.     to the specified value and the end offset set to the selectionExtremeEdge value. */
  241.  
  242. void SetEndSelection(SelectionHandle selection, SelectionOffset fromOffset)
  243.   {
  244.   SelectionOffsetRange  myRange;
  245.   myRange.minOffset = fromOffset;
  246.   myRange.maxOffset = selectionExtremeEdge;
  247.   SetRangeSelection(selection, &myRange);
  248.   } /* SetEndSelection */
  249.  
  250. /* ---------------------------------------------------------------------------------- */
  251.  
  252. /* DisposeSelection disposes of the storage associated with a Selection. */
  253.  
  254. void DisposeSelection(SelectionHandle selection)
  255.   {
  256.   if (selection == nil) {GXPostGraphicsError(parameter_is_nil); return;}
  257.   else DisposHandle((Handle) selection);
  258.   } /* DisposeSelection */
  259.  
  260. /* ---------------------------------------------------------------------------------- */
  261.  
  262. /* GetSelectionType allows procedural access to the type of a Selection. */
  263.  
  264. SelectionType GetSelectionType(SelectionHandle selection)
  265.   {
  266.   SelectionPtr  sip;
  267.   if (selection == nil || (sip = *selection) == nil)
  268.     {GXPostGraphicsError(parameter_is_nil); return emptySelection;}
  269.   else return sip->type;
  270.   } /* GetSelectionType */
  271.  
  272. /* ---------------------------------------------------------------------------------- */
  273.  
  274. /* GetCaretSelection allows procedural access to the character offset and leadingEdge
  275.     value associated with a Selection of type simpleCaret. */
  276.  
  277. SelectionOffset GetCaretSelection(SelectionHandle selection, boolean *leadingEdge)
  278.   {
  279.   SelectionPtr  sip;
  280.   if (selection == nil || (sip = *selection) == nil)
  281.     {GXPostGraphicsError(parameter_is_nil); return 0;}
  282.   if (sip->type != simpleCaret)
  283.     {GXPostGraphicsError(parameter_out_of_range); return 0;}
  284.   /* It is not an error for leadingEdge to be nil; in this case, we assume the client
  285.       wasn't interested in the state of the leadingEdge flag. */
  286.   if (leadingEdge) *leadingEdge = (boolean) sip->data.caret.leadingEdge;
  287.   return sip->data.caret.offset;
  288.   } /* GetCaretSelection */
  289.  
  290. /* ---------------------------------------------------------------------------------- */
  291.  
  292. /* GetRangeSelection allows procedural access to the SelectionOffsetRange value(s)
  293.     associated with a Selection of type simpleRange or discontiguousRange. It returns
  294.     the total byte size needed for the resulting SelectionRanges structure; it returns
  295.     this size even if the given selectionRanges argument is nil. */
  296.  
  297. long GetRangeSelection(SelectionHandle selection, SelectionRanges *selectionRanges)
  298.   {
  299.   long                  totalSize;
  300.   SelectionPtr          sip;
  301.   SelectionOffsetRange  *pDst, *pSrc;
  302.   short                 i;
  303.   if (selection == nil || (sip = *selection) == nil)
  304.     {GXPostGraphicsError(parameter_is_nil); return 0;}
  305.   if (sip->type != simpleRange && sip->type != discontiguousRange)
  306.     {GXPostGraphicsError(parameter_out_of_range); return 0;}
  307.   totalSize = sizeof(SelectionRanges) + (sip->data.range.rangeCount * sizeof(SelectionOffsetRange));
  308.   if (selectionRanges)
  309.     { /* copy the data */
  310.     selectionRanges->rangeCount = sip->data.range.rangeCount;
  311.     i = (short) selectionRanges->rangeCount;
  312.     pDst = &selectionRanges->ranges[0];
  313.     pSrc = &sip->data.range.ranges[0];
  314.     while (i--) *pDst++ = *pSrc++;
  315.     } /* end copy the data */
  316.   return totalSize;
  317.   } /* GetRangeSelection */
  318.  
  319. /* ---------------------------------------------------------------------------------- */
  320.  
  321. /* XXX Still don't have "equalToSelection" logic in here... XXX */
  322.  
  323. /* RangeInSelection returns an indication of the degree to which the specified offset
  324.     range is contained in the specified Selection. */
  325.  
  326. SelectionMatch RangeInSelection(SelectionHandle selection, SelectionOffsetRange *range)
  327.   {
  328.   SelectionHandle   thisRange;
  329.   SelectionMatch    retVal;
  330.   SelectionPtr      sip;
  331.   
  332.   thisRange = NewRangeSelection(range);
  333.   SectSelection(thisRange, selection);
  334.   sip = *thisRange;
  335.   switch (sip->type)
  336.     {
  337.     case emptySelection:
  338.     case simpleCaret: /* "simpleCaret" hadn't better happen here... */
  339.       retVal = notInSelection;
  340.       break;
  341.     case simpleRange:
  342.       if (range->minOffset == sip->data.range.ranges[0].minOffset && range->maxOffset == sip->data.range.ranges[0].maxOffset)
  343.         retVal = fullyInSelection;
  344.       else retVal = partlyInSelection;
  345.       break;
  346.     case discontiguousRange:
  347.       retVal = partlyInSelection; /* is this correct? */
  348.       break;
  349.     }
  350.   
  351.   DisposeSelection(thisRange);
  352.   return retVal;
  353.   } /* RangeInSelection */
  354.  
  355. /* ---------------------------------------------------------------------------------- */
  356.  
  357. /* IsFullSelection determines whether a Selection has the special selectionExtremeEdge
  358.     value as both its minOffset and maxOffset values. */
  359.  
  360. static boolean IsFullSelection(SelectionHandle s)
  361.   {
  362.   SelectionPtr          sip;
  363.   SelectionOffsetRange  *sor;
  364.   
  365.   if (s == nil || (sip = *s) == nil)
  366.     {GXPostGraphicsError(parameter_is_nil); return false;}
  367.   if (sip->type != simpleRange && sip->type != discontiguousRange)
  368.     return false;
  369.   sor = &sip->data.range.ranges[0];
  370.   
  371.   if (sor->minOffset == selectionExtremeEdge && sor->maxOffset == selectionExtremeEdge)
  372.     return true;
  373.   else
  374.     return false;
  375.   
  376.   } /* IsFullSelection */
  377.  
  378. /* ---------------------------------------------------------------------------------- */
  379.  
  380. void InvertSelection(SelectionHandle dest)
  381.   {
  382.   Handle                tempHandle;
  383.   SelectionPtr          destSIP;
  384.   SelectionOffsetRange  *newSOR, *nextSOR, *sor;
  385.   short                 count, newCount;
  386.   
  387.   if (dest == nil || (destSIP = *dest) == nil)
  388.     {GXPostGraphicsError(parameter_is_nil); return;}
  389.   if (destSIP->type == simpleCaret)
  390.     {GXPostGraphicsError(parameter_out_of_range); return;}
  391.   
  392.   if (IsFullSelection(dest)) SetEmptySelection(dest);
  393.   else if (destSIP->type == emptySelection) SetFullSelection(dest);
  394.   else
  395.     { /* neither empty nor full */
  396.     tempHandle = NewHandle((destSIP->data.range.rangeCount + 1) * sizeof(SelectionOffsetRange));
  397.     if (tempHandle == nil)
  398.       {GXPostGraphicsError(out_of_memory); return;}
  399.     destSIP = *dest;  /* memory might have moved */
  400.     sor = &destSIP->data.range.ranges[0];
  401.     nextSOR = sor + 1;
  402.     count = (short) destSIP->data.range.rangeCount;
  403.     newSOR = (SelectionOffsetRange *) *tempHandle;
  404.     newCount = 0;
  405.     
  406.     /* At some gxPoint, we may want to deal with the problem that a non open-ended
  407.         selection that starts at zero doesn't get restored after two inversions. */
  408.     
  409.     /* If first is not open-ended, add [selectionExtremeEdge, first.minOffset] */
  410.     if (sor->minOffset != selectionExtremeEdge)
  411.       {
  412.       newSOR->minOffset = selectionExtremeEdge;
  413.       (newSOR++)->maxOffset = sor->minOffset;
  414.       newCount++;
  415.       }
  416.     /* Start main loop */
  417.     while (--count) /* "--count" insures n-1 iterations */
  418.       { /* main loop */
  419.       newSOR->minOffset = sor->maxOffset;
  420.       (newSOR++)->maxOffset = nextSOR->minOffset;
  421.       newCount++;
  422.       sor = nextSOR++;
  423.       } /* end main loop */
  424.     /* If last is not open-ended, add [last.maxOffset, selectionExtremeEdge] */
  425.     if (sor->maxOffset != selectionExtremeEdge)
  426.       {
  427.       newSOR->minOffset = sor->maxOffset;
  428.       newSOR->maxOffset = selectionExtremeEdge;
  429.       newCount++;
  430.       }
  431.     /* Now change the size of dest (if needed) and store the new SORs into it. */
  432.     if (newCount > (short) destSIP->data.range.rangeCount)
  433.       {
  434.       SetHandleSize((Handle) dest, siSize + newCount * sizeof(SelectionOffsetRange));
  435.       destSIP = *dest;  /* memory might have moved */
  436.       }
  437.     destSIP->data.range.rangeCount = newCount;
  438.     destSIP->type = (SelectionType) ((newCount == 1) ? simpleRange : discontiguousRange);
  439.     sor = &destSIP->data.range.ranges[0];
  440.     newSOR = (SelectionOffsetRange *) *tempHandle;
  441.     while (newCount--) *sor++ = *newSOR++;
  442.     DisposHandle(tempHandle);
  443.     } /* end neither empty nor full */
  444.   
  445.   } /* InvertSelection */
  446.  
  447. /* ---------------------------------------------------------------------------------- */
  448.  
  449. void ShiftSelection(SelectionHandle dest, SelectionOffset delta)
  450.   {
  451.   SelectionOffsetRange  *sor;
  452.   SelectionPtr          destSIP;
  453.   short                 count;
  454.   
  455.   if (dest == nil || (destSIP = *dest) == nil)
  456.     {GXPostGraphicsError(parameter_is_nil); return;}
  457.   
  458.   switch (destSIP->type)
  459.     {
  460.     case emptySelection:
  461.       break;
  462.     case simpleCaret:
  463.       destSIP->data.caret.offset += delta;
  464.       break;
  465.     case simpleRange:
  466.     case discontiguousRange:
  467.       count = (short) destSIP->data.range.rangeCount;
  468.       sor = &destSIP->data.range.ranges[0];
  469.       while (count--)
  470.         {
  471.         if (sor->minOffset != selectionExtremeEdge) sor->minOffset += delta;
  472.         if (sor->maxOffset != selectionExtremeEdge) sor->maxOffset += delta;
  473.         sor++;
  474.         }
  475.       break;
  476.     } /* end switch */
  477.   
  478.   } /* ShiftSelection */
  479.  
  480. /* ---------------------------------------------------------------------------------- */
  481.  
  482. /* GrowSI is an internal work function used to grow a Selection to allow more
  483.     space for SelectionOffsetRanges. */
  484.  
  485. static void GrowSI(
  486.   Handle                h,
  487.   short                 *maxAllocated,
  488.   SelectionPtr          *sip,
  489.   SelectionOffsetRange  **dest,
  490.   SelectionOffsetRange  **prevDest)
  491.  
  492.   {
  493.   short oldMax = *maxAllocated;
  494.   *maxAllocated <<= 1;
  495.   SetHandleSize(h, siSize + (*maxAllocated) * sizeof(SelectionOffsetRange));
  496.   if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  497.   *sip = (SelectionPtr) *h;
  498.   *dest = (*sip)->data.range.ranges + oldMax;
  499.   *prevDest = *dest - 1;
  500.   } /* GrowSI */
  501.  
  502. /* ---------------------------------------------------------------------------------- */
  503.  
  504. /* UnionOne takes two SelectionOffsetRanges and determines if their union is representable
  505.     in a single resulting SelectionOffsetRange. If it is not, the dest value is not set,
  506.     and a value is returned that indicates whether the first or second argument starts
  507.     earlier. If it is, the dest value is set to the union, and the unionOne value is
  508.     returned. */
  509.  
  510. static UnionOneState UnionOne(
  511.   SelectionOffsetRange  *sor1,
  512.   SelectionOffsetRange  *sor2,
  513.   SelectionOffsetRange  *dest)
  514.  
  515.   {
  516.   short         selector;
  517.   UnionOneState retVal;
  518.   
  519.   selector = (short) ((sor1->minOffset == selectionExtremeEdge) << 3);
  520.   selector += (short) ((sor1->maxOffset == selectionExtremeEdge) << 2);
  521.   selector += (short) ((sor2->minOffset == selectionExtremeEdge) << 1);
  522.   selector += (sor2->maxOffset == selectionExtremeEdge);
  523.   
  524.   switch (selector)
  525.     {
  526.     
  527.     /* Case 0 is where neither SelectionOffsetRange is open-ended */
  528.     
  529.     case 0:
  530.       if (sor1->minOffset < sor2->minOffset)
  531.         if (sor2->minOffset <= sor1->maxOffset)
  532.           {
  533.           dest->minOffset = sor1->minOffset;
  534.           dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  535.           retVal = unionOne;
  536.           }
  537.         else retVal = firstIsLow;
  538.       else
  539.         if (sor1->minOffset <= sor2->maxOffset)
  540.           {
  541.           dest->minOffset = sor2->minOffset;
  542.           dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  543.           retVal = unionOne;
  544.           }
  545.         else retVal = secondIsLow;
  546.       break;
  547.     
  548.     /* Case 1 is where the second SelectionOffsetRange is open on the right. */
  549.     
  550.     case 1:
  551.       if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  552.       else
  553.         {
  554.         dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  555.         dest->maxOffset = selectionExtremeEdge;
  556.         retVal = unionOne;
  557.         }
  558.       break;
  559.     
  560.     /* Case 2 is where the second SelectionOffsetRange is open on the left. */
  561.     
  562.     case 2:
  563.       if (sor2->maxOffset < sor1->minOffset) retVal = secondIsLow;
  564.       else
  565.         {
  566.         dest->minOffset = selectionExtremeEdge;
  567.         dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  568.         retVal = unionOne;
  569.         }
  570.       break;
  571.     
  572.     /* Cases 3, 7, and 11 thru 15 are where one (or both) are full selections. */
  573.     
  574.     case 3:
  575.     case 7:
  576.     case 11:
  577.     case 12:
  578.     case 13:
  579.     case 14:
  580.     case 15:
  581.       dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  582.       retVal = unionAll;
  583.       break;
  584.     
  585.     /* Case 4 is where the first SelectionOffsetRange is open on the right. */
  586.     
  587.     case 4:
  588.       if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  589.       else
  590.         {
  591.         dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  592.         dest->maxOffset = selectionExtremeEdge;
  593.         retVal = unionOne;
  594.         }
  595.       break;
  596.     
  597.     /* Case 5 is where both SelectionOffsetRanges are open on the right. */
  598.     
  599.     case 5:
  600.       dest->minOffset = MyMin(sor1->minOffset, sor2->minOffset);
  601.       dest->maxOffset = selectionExtremeEdge;
  602.       retVal = unionOne;
  603.       break;
  604.     
  605.     /* Case 6 is where the first SelectionOffsetRange is open on the right and the
  606.         second SelectionOffsetRange is open on the right. */
  607.     
  608.     case 6:
  609.       if (sor1->minOffset > sor2->maxOffset) retVal = secondIsLow;
  610.       else
  611.         {
  612.         dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  613.         retVal = unionAll;
  614.         }
  615.       break;
  616.     
  617.     /* Case 8 is where the first SelectionOffsetRange is open on the left. */
  618.     
  619.     case 8:
  620.       if (sor1->maxOffset < sor2->minOffset) retVal = firstIsLow;
  621.       else
  622.         {
  623.         dest->minOffset = selectionExtremeEdge;
  624.         dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  625.         retVal = unionOne;
  626.         }
  627.       break;
  628.     
  629.     /* Case 9 is where the first SelectionOffsetRange is open on the left and the
  630.         second SelectionOffsetRange is open on the left. */
  631.     
  632.     case 9:
  633.       if (sor2->minOffset > sor1->maxOffset) retVal = firstIsLow;
  634.       else
  635.         {
  636.         dest->minOffset = dest->maxOffset = selectionExtremeEdge;
  637.         retVal = unionAll;
  638.         }
  639.       break;
  640.     
  641.     /* Case 10 is where both SelectionOffsetRanges are open on the left. */
  642.     
  643.     case 10:
  644.       dest->minOffset = selectionExtremeEdge;
  645.       dest->maxOffset = MyMax(sor1->maxOffset, sor2->maxOffset);
  646.       retVal = unionOne;
  647.       break;
  648.     
  649.     } /* end switch */
  650.   
  651.   return retVal;
  652.   } /* UnionOne */
  653.  
  654. /* ---------------------------------------------------------------------------------- */
  655.  
  656. void UnionSelection(SelectionHandle dest, SelectionHandle source)
  657.   {
  658.   Handle                tempHandle;
  659.   long                  destCount, sor1Count, sor2Count;
  660.   SelectionOffsetRange  *destSOR, *prevDestSOR, *sor1, *sor2, workSORSpace, workSORSpace2;
  661.   SelectionPtr          destSIP, newSIP, sourceSIP;
  662.   short                 maxAllocated = 10;
  663.   Size                  newSize, sourceSize;
  664.   
  665.   /* Allocate the memory first, since we want it to be the last thing that moves mem. */
  666.   
  667.   newSize = siSize + maxAllocated * sizeof(SelectionOffsetRange);
  668.   tempHandle = NewHandle(newSize);
  669.   if (tempHandle == nil)
  670.     {GXPostGraphicsError(out_of_memory); return;}
  671.   destCount = 0;
  672.   newSIP = (SelectionPtr) *tempHandle;
  673.   
  674.   /* Validate inputs. */
  675.   
  676.   if (dest == nil || (destSIP = *dest) == nil)
  677.     {GXPostGraphicsError(parameter_is_nil); DisposHandle(tempHandle); return;}
  678.   if (source == nil || (sourceSIP = *source) == nil)
  679.     {GXPostGraphicsError(parameter_is_nil); DisposHandle(tempHandle); return;}
  680.   if (destSIP->type == simpleCaret || sourceSIP->type == simpleCaret)
  681.     {GXPostGraphicsError(parameter_out_of_range); DisposHandle(tempHandle); return;}
  682.   
  683.   /* If both the Selections are empty, just return. */
  684.   
  685.   if ((destSIP->type == emptySelection) && (sourceSIP->type == emptySelection))
  686.     {DisposHandle(tempHandle); return;}
  687.   
  688.   /* Handle cases where one (but not both) selection is empty. If dest is empty, this
  689.       means just copy source to dest. If source is empty, just return. */
  690.   
  691.   if ((destSIP->type == emptySelection) && (sourceSIP->type != emptySelection))
  692.     { /* copy source to dest */
  693.     sourceSize = GetHandleSize((Handle) source);
  694.     SetHandleSize((Handle) dest, sourceSize);
  695.     if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  696.     else BlockMove(*((Handle) source), *((Handle) dest), sourceSize); /* mem might have moved! */
  697.     DisposHandle(tempHandle);
  698.     return;
  699.     } /* end copy source to dest */
  700.   else if ((destSIP->type != emptySelection) && (sourceSIP->type == emptySelection))
  701.     {DisposHandle(tempHandle); return;}
  702.   
  703.   /* At this gxPoint both selections are range selections. We can therefore begin to walk
  704.       down the two selections in parallel, creating the new selection from the combo.
  705.       We "seed" the process by creating the first new entry before entering the loop;
  706.       this lets us get some of the testing code out of the loop proper. */
  707.   
  708.   prevDestSOR = destSOR = &newSIP->data.range.ranges[0];
  709.   sor1 = &destSIP->data.range.ranges[0];
  710.   sor2 = &sourceSIP->data.range.ranges[0];
  711.   sor1Count = destSIP->data.range.rangeCount;
  712.   sor2Count = sourceSIP->data.range.rangeCount;
  713.   destCount = 1;
  714.   
  715.   HLock((Handle) dest);
  716.   HLock((Handle) source);
  717.   
  718.   switch (UnionOne(sor1, sor2, &workSORSpace))
  719.     {
  720.     case unionAll:
  721.       *destSOR = workSORSpace;
  722.       sor1Count = sor2Count = 0;  /* forces following loop to be skipped */
  723.       break;
  724.     case unionOne:
  725.       *destSOR++ = workSORSpace;
  726.       sor1Count--; sor1++;
  727.       sor2Count--; sor2++;
  728.       break;
  729.     case firstIsLow:
  730.       *destSOR++ = *sor1++;
  731.       sor1Count--;
  732.       break;
  733.     case secondIsLow:
  734.       *destSOR++ = *sor2++;
  735.       sor2Count--;
  736.       break;
  737.     } /* end switch */
  738.   
  739.   while ((sor1Count > 0) || (sor2Count > 0))
  740.     { /* main merge loop */
  741.     if ((sor1Count > 0) && (sor2Count > 0))
  742.       { /* still have both */
  743.       switch (UnionOne(sor1, sor2, &workSORSpace))
  744.         {
  745.         case unionAll:
  746.           newSIP->data.range.ranges[0] = workSORSpace;
  747.           destCount = 1;
  748.           sor1Count = sor2Count = 0;
  749.           break;
  750.         case unionOne:
  751.           switch (UnionOne(prevDestSOR, &workSORSpace, &workSORSpace2))
  752.             {
  753.             case unionAll:
  754.               newSIP->data.range.ranges[0] = workSORSpace;
  755.               destCount = 1;
  756.               sor1Count = sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  757.               break;
  758.             case unionOne:
  759.               *prevDestSOR = workSORSpace2;
  760.               break;
  761.             case firstIsLow:
  762.               if (destCount == maxAllocated)
  763.                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  764.               *destSOR = workSORSpace;
  765.               destCount++;
  766.               prevDestSOR = destSOR++;
  767.               break;
  768.             case secondIsLow:
  769.               GXPostGraphicsError(internal_layout_error);
  770.               sor1Count = sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  771.               break;
  772.             } /* end switch */
  773.           sor1++; sor2++;
  774.           sor1Count--; sor2Count--;
  775.           break;
  776.         case firstIsLow:
  777.           switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  778.             {
  779.             case unionAll:
  780.               newSIP->data.range.ranges[0] = workSORSpace;
  781.               destCount = 1;
  782.               sor1Count = 1;  /* dec'd to 0 below, forces loop exit */
  783.               sor2Count = 0;
  784.               break;
  785.             case unionOne:
  786.               *prevDestSOR = workSORSpace;
  787.               break;
  788.             case firstIsLow:
  789.               if (destCount == maxAllocated)
  790.                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  791.               *destSOR = *sor1;
  792.               destCount++;
  793.               prevDestSOR = destSOR++;
  794.               break;
  795.             case secondIsLow:
  796.               GXPostGraphicsError(internal_layout_error);
  797.               sor1Count = 1;  /* dec'd to 0 below, forces loop exit */
  798.               break;
  799.             } /* end switch */
  800.           sor1++;
  801.           sor1Count--;
  802.           break;
  803.         case secondIsLow:
  804.           switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  805.             {
  806.             case unionAll:
  807.               newSIP->data.range.ranges[0] = workSORSpace;
  808.               destCount = 1;
  809.               sor1Count = 0;
  810.               sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  811.               break;
  812.             case unionOne:
  813.               *prevDestSOR = workSORSpace;
  814.               break;
  815.             case firstIsLow:
  816.               if (destCount == maxAllocated)
  817.                 GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  818.               *destSOR = *sor2;
  819.               destCount++;
  820.               prevDestSOR = destSOR++;
  821.               break;
  822.             case secondIsLow:
  823.               GXPostGraphicsError(internal_layout_error);
  824.               sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  825.               break;
  826.             } /* end switch */
  827.           sor2++;
  828.           sor2Count--;
  829.           break;
  830.         } /* end switch */
  831.       } /* end still have both */
  832.     else if (sor1Count > 0)
  833.       { /* still have first */
  834.       switch (UnionOne(prevDestSOR, sor1, &workSORSpace))
  835.         {
  836.         case unionAll:
  837.           newSIP->data.range.ranges[0] = workSORSpace;
  838.           destCount = 1;
  839.           sor1Count = 1;  /* dec'd to 0 below, forces loop exit */
  840.           sor2Count = 0;
  841.           break;
  842.         case unionOne:
  843.           *prevDestSOR = workSORSpace;
  844.           break;
  845.         case firstIsLow:
  846.           if (destCount == maxAllocated)
  847.             GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  848.           *destSOR = *sor1;
  849.           destCount++;
  850.           prevDestSOR = destSOR++;
  851.           break;
  852.         case secondIsLow:
  853.           GXPostGraphicsError(internal_layout_error);
  854.           sor1Count = 1;  /* dec'd to 0 below, forces loop exit */
  855.           break;
  856.         } /* end switch */
  857.       sor1++;
  858.       sor1Count--;
  859.       } /* end still have first */
  860.     else
  861.       { /* still have second */
  862.       switch (UnionOne(prevDestSOR, sor2, &workSORSpace))
  863.         {
  864.         case unionAll:
  865.           newSIP->data.range.ranges[0] = workSORSpace;
  866.           destCount = 1;
  867.           sor1Count = 0;
  868.           sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  869.           break;
  870.         case unionOne:
  871.           *prevDestSOR = workSORSpace;
  872.           break;
  873.         case firstIsLow:
  874.           if (destCount == maxAllocated)
  875.             GrowSI(tempHandle, &maxAllocated, &newSIP, &destSOR, &prevDestSOR);
  876.           *destSOR = *sor2;
  877.           destCount++;
  878.           prevDestSOR = destSOR++;
  879.           break;
  880.         case secondIsLow:
  881.           GXPostGraphicsError(internal_layout_error);
  882.           sor2Count = 1;  /* dec'd to 0 below, forces loop exit */
  883.           break;
  884.         } /* end switch */
  885.       sor2++;
  886.       sor2Count--;
  887.       } /* end still have second */
  888.     } /* end main merge loop */
  889.   
  890.   HUnlock((Handle) dest);
  891.   HUnlock((Handle) source);
  892.   
  893.   newSize = siSize + destCount * sizeof(SelectionOffsetRange);
  894.   SetHandleSize((Handle) dest, newSize);
  895.   if (MemError() != noErr) GXPostGraphicsError(out_of_memory);
  896.   destSIP = *dest;
  897.   
  898.   destSIP->type = (SelectionType) ((destCount == 1) ? simpleRange : discontiguousRange);
  899.   destSIP->data.range.rangeCount = destCount;
  900.   sor1 = &newSIP->data.range.ranges[0];
  901.   destSOR = &destSIP->data.range.ranges[0];
  902.   while (destCount-- > 0) *destSOR++ = *sor1++;
  903.   
  904.   DisposHandle(tempHandle);
  905.   
  906.   } /* UnionSelection */
  907.  
  908. /* ---------------------------------------------------------------------------------- */
  909.  
  910. void SectSelection(SelectionHandle dest, SelectionHandle source)
  911.   {
  912.   SelectionHandle sourceCopy = source;
  913.   if (HandToHand((Handle *) &sourceCopy) != noErr)
  914.     {GXPostGraphicsError(out_of_memory); return;}
  915.   InvertSelection(dest);
  916.   InvertSelection(sourceCopy);
  917.   UnionSelection(dest, sourceCopy);
  918.   DisposHandle((Handle) sourceCopy);
  919.   InvertSelection(dest);
  920.   } /* SectSelection */
  921.  
  922. /* ---------------------------------------------------------------------------------- */
  923.  
  924. void XorSelection(SelectionHandle dest, SelectionHandle source)
  925.   {
  926.   SelectionHandle sourceCopy = source;
  927.   if (HandToHand((Handle *) &sourceCopy) != noErr)
  928.     {GXPostGraphicsError(out_of_memory); return;}
  929.   SectSelection(sourceCopy, dest);
  930.   UnionSelection(dest, source);
  931.   DiffSelection(dest, sourceCopy);
  932.   DisposHandle((Handle) sourceCopy);
  933.   } /* XorSelection */
  934.  
  935. /* ---------------------------------------------------------------------------------- */
  936.  
  937. void DiffSelection(SelectionHandle dest, SelectionHandle source)
  938.   {
  939.   SelectionHandle sourceCopy = source;
  940.   if (HandToHand((Handle *) &sourceCopy) != noErr)
  941.     {GXPostGraphicsError(out_of_memory); return;}
  942.   InvertSelection(sourceCopy);
  943.   SectSelection(dest, sourceCopy);
  944.   DisposHandle((Handle) sourceCopy);
  945.   } /* DiffSelection */
  946.  
  947. /* ---------------------------------------------------------------------------------- */
  948.  
  949. gxShape GetLayoutSelection(
  950.   gxShape           layout,
  951.   SelectionHandle selection,
  952.   SelectionOffset lineStart,
  953.   gxHighlightType   highlightType,
  954.   gxCaretType       caretType)
  955.  
  956.   {
  957.   long                  maxOffset;
  958.   SelectionOffset       off1, off2;
  959.   SelectionOffsetRange  *sor;
  960.   SelectionPtr          destSIP;
  961.   gxShape                 retShape, workShape;
  962.   short                 count;
  963.   
  964.   if (layout == nil || selection == nil || (destSIP = *selection) == nil)
  965.     {GXPostGraphicsError(parameter_is_nil); return nil;}
  966.   
  967.   HLock((Handle) selection);
  968.   maxOffset = GXGetLayout(layout, nil, nil, nil, nil, nil, nil, nil, nil, nil);
  969.   
  970.   switch (destSIP->type)
  971.     {
  972.     case emptySelection:
  973.       retShape = GXNewShape(gxEmptyType);
  974.       break;
  975.     case simpleCaret:
  976.       off1 = destSIP->data.caret.offset - lineStart;
  977.       if (off1 < 0) off1 = 0;
  978.       else if (off1 > maxOffset) off1 = maxOffset;
  979.       retShape = GXGetLayoutCaret(
  980.         layout,
  981.         (gxByteOffset) off1,
  982.         highlightType,
  983.         caretType,
  984.         nil);
  985.       break;
  986.     case simpleRange:
  987.       if (destSIP->data.range.ranges[0].minOffset == selectionExtremeEdge) off1 = 0;
  988.       else
  989.         {
  990.         off1 = destSIP->data.range.ranges[0].minOffset - lineStart;
  991.         if (off1 < 0) off1 = 0;
  992.         else if (off1 > maxOffset) off1 = maxOffset;
  993.         }
  994.       if (destSIP->data.range.ranges[0].maxOffset == selectionExtremeEdge) off2 = maxOffset;
  995.       else
  996.         {
  997.         off2 = destSIP->data.range.ranges[0].maxOffset - lineStart;
  998.         if (off2 < 0) off2 = 0;
  999.         else if (off2 > maxOffset) off2 = maxOffset;
  1000.         }
  1001.       retShape = GXGetLayoutHighlight(
  1002.         layout,
  1003.         (gxByteOffset) off1,
  1004.         (gxByteOffset) off2,
  1005.         highlightType,
  1006.         nil);
  1007.       break;
  1008.     case discontiguousRange:
  1009.       /* In this case, we need to build a multi-gxPolygon gxPolygons gxShape. */
  1010.       count = (short) destSIP->data.range.rangeCount;
  1011.       sor = &destSIP->data.range.ranges[0];
  1012.       retShape = GXNewShape(gxEmptyType);
  1013.       workShape = GXNewShape(gxEmptyType);
  1014.       while (count--)
  1015.         { /* gather loop */
  1016.         if (sor->minOffset == selectionExtremeEdge) off1 = 0;
  1017.         else
  1018.           {
  1019.           off1 = sor->minOffset - lineStart;
  1020.           if (off1 < 0) off1 = 0;
  1021.           else if (off1 > maxOffset) off1 = maxOffset;
  1022.           }
  1023.         if (sor->maxOffset == selectionExtremeEdge) off2 = maxOffset;
  1024.         else
  1025.           {
  1026.           off2 = sor->maxOffset - lineStart;
  1027.           if (off2 < 0) off2 = 0;
  1028.           else if (off2 > maxOffset) off2 = maxOffset;
  1029.           }
  1030.         GXGetLayoutHighlight(
  1031.           layout,
  1032.           (gxByteOffset) off1,
  1033.           (gxByteOffset) off2,
  1034.           highlightType,
  1035.           workShape);
  1036.         GXUnionShape(retShape, workShape);
  1037.         sor++;
  1038.         } /* end gather loop */
  1039.       GXDisposeShape(workShape);
  1040.       break;
  1041.     } /* end switch */
  1042.   
  1043.   HUnlock((Handle) selection);
  1044.   return retShape;
  1045.   } /* GetLayoutSelection */
  1046.  
  1047. /* NewDiscontiguousSelection creates a discontiguous selection. It returns an empty
  1048.     selection if the argument is nil. */
  1049.  
  1050. SelectionHandle NewDiscontiguousSelection(SelectionRanges *ranges)
  1051.   {
  1052.   long                  i;
  1053.   SelectionHandle       returnedSelection, workingSelection;
  1054.   SelectionOffsetRange  *thisSOR;
  1055.   
  1056.   returnedSelection = NewEmptySelection();
  1057.   
  1058.   if (ranges)
  1059.     {
  1060.     thisSOR = ranges->ranges;
  1061.     workingSelection = NewRangeSelection(thisSOR++);
  1062.     
  1063.     for (i = ranges->rangeCount - 1; i >= 0; --i)
  1064.       {
  1065.       UnionSelection(returnedSelection, workingSelection);
  1066.       SetRangeSelection(workingSelection, thisSOR++);
  1067.       }
  1068.     
  1069.     DisposeSelection(workingSelection);
  1070.     }
  1071.   
  1072.   return returnedSelection;
  1073.   } /* NewDiscontiguousSelection */
  1074.